<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Input &amp; output in Tcl</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Input &amp; output Tcl, learn Tcl, programming, language, Linux">
<meta name="description" content="This chapter of the Tcl tutorial covers input &amp; output in Tcl.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>
<!-- http://www.tcl.tk/man/tcl/tutorial/Tcl26.html -->
<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>


<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>



<h1>Input &amp; output</h1>

<p>
In this chapter, we will work with input, output operations in Tcl.
Tcl has several commands for doing io. We will cover a few of them. 
</p>

<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* LargeSquare */
google_ad_slot = "5070696322";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>

<p>
Tcl uses objects called channels to read and write data. The channels
can be created using the <code>open</code> or <code>socket</code> command.
There are three standard channels, which are available to Tcl scripts
without explicitly creating them. They are automatically opened
by the OS for each new application. They are <code>stdin</code>,
<code>stdout</code> and <code>stderr</code>. The standard input, 
<code>stdin</code>, is used by the scripts to read data. The standard
output, <code>stdout</code>, is used by scripts to write data. The
standard error <code>stderr</code> is used by scripts to write error
messages. 
</p>

<p>
In the first example, we will work with the <code>puts</code>
command. It has the following synopsis:
</p>

<pre>
puts ?-nonewline? ?channelId? string
</pre>

<p>
The channelId is the channel, where we want to write text. The
channelId is optional. If not specified, the default stdout is
assumed.
</p>

<pre class="code">
$ cat print.tcl
#!/usr/bin/tclsh

puts "Message 1"
puts stdout "Message 2"
puts stderr "Message 3"
</pre>

<p>
The <code>puts</code> command writes text to the channel. 
</p>

<pre class="explanation">
puts "Message 1"
</pre>

<p>
If we do not specify the channelId, we write to stdout by default.
</p>

<pre class="explanation">
puts stdout "Message 2"
</pre>

<p>
This line does the same thing as the previous one. We only have
explicitly specified the channelId. 
</p>

<pre class="explanation">
puts stderr "Message 3"
</pre>

<p>
We write to the standard error channel. The error messages
go to the terminal by default. 
</p>

<pre>
$ ./print.tcl
Message 1
Message 2
Message 3
</pre>

<p>
Output. 
</p>

<hr class="btm">

<p>
In the next example, we will be reading from the
standard input channel. 
</p>

<pre class="code">
#!/usr/bin/tclsh

puts -nonewline "Enter your name: "
flush stdout
set name [gets stdin]

puts "Hello $name"
</pre>

<p>
The script asks for input from the user and then
prints a message.
</p>

<pre class="explanation">
puts -nonewline "Enter your name: "
</pre>

<p>
The <code>puts</code> command is used to print messages to the
terminal. But the command can write to any channel, not just 
terminal. 
</p>

<pre class="explanation">
flush stdout
</pre>

<p>
Tcl buffers output internally, so characters written with puts 
may not appear immediately on the output file or device. 
We can force output to appear immediately with the 
<code>flush</code> command.
</p>

<pre class="explanation">
set name [gets stdin]
</pre>

<p>
The <code>gets</code> command reads a line from a channel. 
</p>

<pre>
$ ./hello.tcl
Enter your name: Jan
Hello Jan
</pre>

<p>
Sample output of the script. 
</p>

<hr class="btm">

<p>
The <code>read</code> command is used to read data from the channel. 
</p>

<pre class="code">
#!/usr/bin/tclsh

set c [read stdin 1]

while {$c != "q"} {

    puts -nonewline "$c"
    set c [read stdin 1]
}
</pre>

<p>
The script reads a character from the standard input channel and
then writes it to the standard output until it encounters a q character. 
</p>

<pre class="explanation">
set c [read stdin 1]
</pre>

<p>
We read one character from the standard input channel (stdin). 
</p>

<pre class="explanation">
while {$c != "q"} {
</pre>

<p>
We continue reading characters until the q is pressed.
</p>


<hr class="btm">

<p>
Tcl has <code>pwd</code> and <code>cd</code> commands, similar to 
shell commands. The <code>pwd</code> command returns the current working
directory and the <code>cd</code> command is used to change the working
directory. 
</p>

<pre class="code">
#!/usr/bin/tclsh

set dir [pwd]

puts $dir

cd ..

set dir [pwd]
puts $dir
</pre>

<p>
In this script, we will print the current working directory.
We change the working directory and print the working directory
again. 
</p>

<pre class="explanation">
set dir [pwd]
</pre>

<p>
The <code>pwd</code> command returns the current working directory.
</p>

<pre class="explanation">
cd ..
</pre>

<p>
We change the working directory to the parent of the current directory.
We use the <code>cd</code> command. 
</p>

<pre>
$ ./cwd.tcl 
/home/vronskij/programming/tcl/io
/home/vronskij/programming/tcl
</pre>

<p>
Output. 
</p>

<hr class="btm">

<p>
Tcl has a <code>glob</code> command, which returns the
names of the files that match a pattern.
</p>


<pre class="code">
#!/usr/bin/tclsh

set files [glob *.tcl]

foreach file $files {

    puts $file
}
</pre>

<p>
The script prints all files with .tcl extension to the
console. 
</p>

<pre class="explanation">
set files [glob *.tcl]
</pre>

<p>
The <code>glob</code> command returns a list of files
that match the *.tcl pattern. 
</p>

<pre class="explanation">
foreach file $files {

    puts $file
}
</pre>

<p>
We go through the list of files and print each item of the list
to the console.
</p>

<pre>
$ ./glob.tcl
isfile.tcl
readfile.tcl
attributes.tcl
allfiles.tcl
cwd.tcl
addnumbers.tcl
glob.tcl
write2file.tcl
files.tcl
</pre>

<p>
An example from a certain directory. 
</p>

<hr class="btm">

<p>
In the following code example, we are going to check if
a file name is a regular file or a directory. 
</p>

<pre class="code">
#!/usr/bin/tclsh

set files [glob *]

foreach fl $files {

    if {[file isfile $fl]} {
        
        puts "$fl is a file"
    } elseif { [file isdirectory $fl]} {
        
        puts "$fl is a directory"
    }
}
</pre>

<p>
We go through all file names in the current working directory
and print whether it is a file or a directory. 
</p>

<pre class="explanation">
set files [glob *]
</pre>

<p>
Using the <code>glob</code> command we create a list of 
file and directory names of a current directory. 
</p>

<pre class="explanation">
if {[file isfile $fl]} {
</pre>

<p>
We execute the body of the if command, if 
the file name in question is a file.
</p>

<pre class="explanation">
} elseif { [file isdirectory $fl]} {
</pre>

<p>
The <code>file isdirectory</code> command determines,
whether a file name is a directory. Note that on Unix,
a directory is a special case of a file. 
</p>

<hr class="btm">

<p>
Next, we are going to write to a file. 
</p>

<pre class="code">
#!/usr/bin/tclsh

set fp [open days w]

set days {Monday Tuesday Wednesday Thursday Friday Saturday Sunday}

puts $fp $days
close $fp
</pre>

<p>
In the script, we open a file for writing. We write days of a week to
a file.
</p>

<pre class="explanation">
set fp [open days w]
</pre>

<p>
We open a file named "days" for writing. The <code>open</code>
command returns a channel id. 
</p>

<pre class="explanation">
set days {Monday Tuesday Wednesday Thursday Friday Saturday Sunday}
</pre>

<p>
This data is going to be written to the file. 
</p>

<pre class="explanation">
puts $fp $days
</pre>

<p>
We used the channel id returned by the <code>open</code> command to
write to the file. 
</p>

<pre class="explanation">
close $fp
</pre>

<p>
We close the opened channel. 
</p>

<pre>
$ ./write2file.tcl
$ cat days 
Monday Tuesday Wednesday Thursday Friday Saturday Sunday
</pre>

<p>
We run the script and check the contents of the days file. 
</p>

<hr class="btm">

<p>
In the following script, we are going to read data
from a file. 
</p>

<pre>
$ cat languages 
Python
Tcl
Visual Basic
Perl
Java
C
C#
Ruby
Scheme
</pre>

<p>
We have a simple file called languages in a directory. 
</p>

<pre class="code">
#!/usr/bin/tclsh

set fp [open languages r]
set data [read $fp]

puts -nonewline $data

close $fp
</pre>

<p>

</p>

<pre class="explanation">
set fp [open languages r]
</pre>

<p>
We create a channel by opening the languages file in a read-only mode.
</p>

<pre class="explanation">
set data [read $fp]
</pre>

<p>
If we do not provide a second parameter to the <code>read</code>
command, it reads all data from the file until the end of the file. 
</p>

<pre class="explanation">
puts -nonewline $data
</pre>

<p>
We print the data to the console. 
</p>

<pre class="explanation">
$ ./readfile.tcl 
Python
Tcl
Visual Basic
Perl
Java
C
C#
Ruby
Scheme
</pre>

<p>
Sample run of the readfile.tcl command. 
</p>

<hr class="btm">

<p>
The next script performs some additional file operations. 
</p>

<pre class="code">
#!/usr/bin/tclsh

set fp [open newfile w]

puts $fp "this is new file"
flush $fp

file copy newfile newfile2
file delete newfile

close $fp
</pre>

<p>
We open a file and write some text to it. The file is copied. The
original file is then deleted. 
</p>

<pre class="explanation">
file copy newfile newfile2
</pre>

<p>
The <code>file copy</code> command copies a file. 
</p>

<pre class="explanation">
file delete newfile
</pre>

<p>
The original file is deleted with the <code>file delete</code> command.
</p>

<hr class="btm">

<p>
In the final example, we will work with file attributes.
</p>

<pre class="code">
#!/usr/bin/tclsh

set files [glob *]

set mx 0

foreach fl $files {
    
    set len [string length $fl]

    if { $len > $mx} {
        
        set mx $len
    }
}

set fstr "%-$mx\s %-s"
puts [format $fstr Name Size]

set fstr "%-$mx\s %d bytes"
foreach fl $files {

    set size [file size $fl]

    puts [format $fstr $fl $size]

}
</pre>

<p>
The script creates two columns. In the first column, we have
the name of the file. In the second column, we display 
the size of the file. 
</p>

<pre class="explanation">
foreach fl $files {
    
    set len [string length $fl]

    if { $len > $mx} {
        
        set mx $len
    }
}
</pre>

<p>
In this loop, we find out the most lengthy file name. 
This will be used when formatting the output columns. 
</p>

<pre class="explanation">
set fstr "%-$mx\s %-s"
puts [format $fstr Name Size]
</pre>

<p>
Here we print the headers of the columns. 
To format the data, we use the <code>format</code> command. 
</p>

<pre class="explanation">
set fstr "%-$mx\s %d bytes"
foreach fl $files {

    set size [file size $fl]

    puts [format $fstr $fl $size]

}
</pre>

<p>
We go through the list of files and print each file name and its
size. The <code>file size</code> command determines the size of
the file. 
</p>

<pre>
$ ./attributes.tcl 
Name            Size
isfile.tcl      219 bytes
readfile.tcl    98 bytes
tmp             4096 bytes
attributes.tcl  337 bytes
allfiles.tcl    75 bytes
read.tcl~       114 bytes
cwd.tcl         76 bytes
</pre>

<p>
Sample run. 
</p>


<p>
In this chapter, we have covered Input/Output operations in Tcl. 
</p>

<br>
<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified April 14, 2011  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

